﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text.RegularExpressions;
using Biblio2.Configuration;

namespace Biblio2.LocalParsing
{
    class FileNameParser : BackgroundWorker
    {
        #region Attributs
        private List<Regex> m_regularExpressionsList;
        private Dictionary<string, string> m_replacementsBefore;
        private Dictionary<string, string> m_replacementsAfter;
        #endregion

        #region Constructeur
        public FileNameParser()
        {
            WorkerReportsProgress = true;
            WorkerSupportsCancellation = true;
            m_regularExpressionsList = new List<Regex>();
            m_replacementsBefore = new Dictionary<string, string>();
            m_replacementsAfter = new Dictionary<string, string>();
        }
        #endregion Constructeur

        #region DoWork
        protected override void OnDoWork(DoWorkEventArgs e)
        {
            List<PathPair> pathPairList = e.Argument as List<PathPair>;
            if (pathPairList != null)
            {
                e.Result = ParsePathPairs(pathPairList);
            }

            if (e.Result == null)
                e.Cancel = true;
        }
        #endregion DoWork
        
        #region Parse FileName
        private List<LocalParseResult> ParsePathPairs(List<PathPair> aPathPairList)
        {
            List<LocalParseResult> listResults = new List<LocalParseResult>();
            int iCpt = 1;
            LocalParseResult result;
            foreach (PathPair pair in aPathPairList)
            {
                if (CancellationPending)
                    return null;
                ReportProgress(0, new WorkerEventProgress(WorkerEventProgress.ReportEnum.Progress, LocalParsingManager.Step.Step1_3, iCpt, aPathPairList.Count, String.Empty));
                result = new LocalParseResult(pair);
                ParsePathPair(result);
                result.CheckValidity();
                listResults.Add(result);
                iCpt++;
            }
            ReportProgress(0, new WorkerEventProgress(WorkerEventProgress.ReportEnum.Progress, LocalParsingManager.Step.Step1_3, iCpt - 1, aPathPairList.Count, String.Empty));

            return listResults;
        }

        private void ParsePathPair(LocalParseResult aResult)
        {
            try
            {
                // Init the Regular Expression list if empty
                if (m_regularExpressionsList.Count == 0)
                    LoadExpressionsList();

                int index = 0;

                String m_FileNameAfterReplacement = aResult.PathPair.FileName.ToLower();
                // run Before replacements
                
                foreach (KeyValuePair<string, string> replacement in m_replacementsBefore)
                    m_FileNameAfterReplacement = m_FileNameAfterReplacement.Replace(replacement.Key, replacement.Value);

                foreach (Regex regularExpression in m_regularExpressionsList)
                {
                    Match matchResults;
                    matchResults = regularExpression.Match(m_FileNameAfterReplacement);

                    if (matchResults.Success)
                    {
                        for (int i = 1; i < matchResults.Groups.Count; i++)
                        {
                            string GroupName = regularExpression.GroupNameFromNumber(i);
                            string GroupValue = matchResults.Groups[i].Value;

                            if (GroupValue.Length > 0 && GroupName != "unknown")
                            {
                                // ´run after replacements on captures
                                foreach (KeyValuePair<string, string> replacement in m_replacementsAfter)
                                    GroupValue = GroupValue.Replace(replacement.Key, replacement.Value);

                                GroupValue = GroupValue.Trim();
                                aResult.SetValue(GroupName, GroupValue);
                            }
                        }
                        String sTemp = GetEpisodeFormat(aResult.PathPair.FileName);
                        if (!sTemp.Equals(String.Empty))
                            aResult.SetValue(LocalParseResult.csEpisodeFormat, sTemp);

                        sTemp = GetEpisodeTeam(aResult.PathPair.FileName);
                        if (!sTemp.Equals(String.Empty))
                            aResult.SetValue(LocalParseResult.csEpisodeTeam, sTemp);
                        
                        return;
                    }
                    index++;
                }
            }
            catch (Exception e)
            {
                ReportProgress(0, new WorkerEventProgress(WorkerEventProgress.ReportEnum.Error, LocalParsingManager.Step.Step1_3, 0, 0, "An error occured in the 'FilenameParser' function (" + e.ToString() + ")"));
            }
        }
        #endregion Parse FileName

        #region Methodes Utilitaires
        private String GetEpisodeFormat(String sFilename)
        {
            List<String> listFormats = ConfigurationManager.FilesConfiguration.FormatsList;

            foreach (String format in listFormats)
                if (sFilename.ToLower().Contains(format.ToLower()))
                    return format;
            
            return String.Empty;
        }

        private String GetEpisodeTeam(String sFilename)
        {
            List<String> listTeams = ConfigurationManager.FilesConfiguration.TeamsList;

            foreach (String team in listTeams)
                if (sFilename.ToLower().Contains(team.ToLower()))
                    return team;
            
            return String.Empty;
        }
        #endregion Methodes Utilitaires
        
        #region Regex
        public void InitListRegularExpressions()
        {
            if (m_regularExpressionsList != null)
                m_regularExpressionsList.Clear();

        }
        

        // Build the list of all the regular expression to use
        private bool LoadExpressionsList()
        {
            bool noError = true;

            try
            {
                if (m_regularExpressionsList == null)
                    m_regularExpressionsList = new List<Regex>();

                List<Expression> listExpressions = ConfigurationManager.FilesConfiguration.ExpressionsList;
                m_replacementsBefore.Clear();
                m_replacementsAfter.Clear();

                foreach (Expression expression in listExpressions)
                {
                    if (expression.isEnabled)
                    {
                        String sExpression = String.Empty;
                        switch (expression.Type)
                        {
                            case Expression.cType_Simple:
                                sExpression = ConvertSimpleExpressionToRegEx(expression.ExpressionValue);
                                break;

                            case Expression.cType_Regexp:
                                sExpression = expression.ExpressionValue;
                                break;
                        }

                        sExpression = sExpression.ToLower();
                        // replace series, season and episode by the valid DBEpisode column names
                        sExpression = sExpression.Replace("<series>", "<" + LocalParseResult.csSerieName + ">");
                        sExpression = sExpression.Replace("<season>", "<" + LocalParseResult.csSeasonIndex + ">");
                        sExpression = sExpression.Replace("<episode>", "<" + LocalParseResult.csEpisodeIndex + ">");
                        sExpression = sExpression.Replace("<episode2>", "<" + LocalParseResult.csEpisodeIndex2 + ">");
                        sExpression = sExpression.Replace("<title>", "<" + LocalParseResult.csEpisodeName + ">");

                        // we precompile the expressions here which is faster in the end
                        try
                        {
                            m_regularExpressionsList.Add(new Regex(sExpression, RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture | RegexOptions.Compiled));
                        }
                        catch (Exception)
                        {
                            ReportProgress(0, new WorkerEventProgress(WorkerEventProgress.ReportEnum.Error, LocalParsingManager.Step.Step1_3, 0, 0, "Impossible d'initialiser les Expressions Regulieres des fichiers"));
                            return false;
                        }
                    }
                }
            }
            catch (Exception)
            {
                return false;
            }

            // now go for the replacements
            try
            {
                foreach (Replacement replacement in ConfigurationManager.FilesConfiguration.ReplacementsList)
                {
                    String searchString = replacement.getToReplace();
                    searchString = searchString
                        .ToLower()
                        .Replace("<space>", " ");

                    String replaceString = replacement.getWith();
                    replaceString = replaceString
                        .ToLower()
                        .Replace("<space>", " ")
                        .Replace("<empty>", "");

                    if (replacement.getBefore() == 1)
                        m_replacementsBefore.Add(searchString, replaceString);
                    else
                        m_replacementsAfter.Add(searchString, replaceString);
                }
                return noError;
            }
            catch (Exception)
            {
                return true;
            }
        }
        
        // Convert a simple expression into a regular expression
        private string ConvertSimpleExpressionToRegEx(string SimpleExpression)
        {
            string field = "";
            string finalRegEx = "";
            int openTagLocation = -1;
            int closeTagLocation = 0;

            SimpleExpression = SimpleExpression.Replace(@"\", @"\\");
            SimpleExpression = SimpleExpression.Replace(".", @"\.");


            while (true)
            {
                openTagLocation = SimpleExpression.IndexOf('<', closeTagLocation);

                if (openTagLocation == -1)
                {
                    if (closeTagLocation > 0)
                        finalRegEx += SimpleExpression.Substring(closeTagLocation + 1);
                    else
                        finalRegEx += SimpleExpression;

                    break;
                }

                if (closeTagLocation == 0)
                    finalRegEx = SimpleExpression.Substring(0, openTagLocation);
                else
                    finalRegEx += SimpleExpression.Substring(closeTagLocation + 1, openTagLocation - closeTagLocation - 1);

                closeTagLocation = SimpleExpression.IndexOf('>', openTagLocation);

                field = SimpleExpression.Substring(openTagLocation + 1, closeTagLocation - openTagLocation - 1);

                if (field.Length > 0)
                {
                    // other tags coming? put lazy *, otherwise put a greedy one
                    if (SimpleExpression.IndexOf('<', closeTagLocation) != -1)
                        finalRegEx += String.Format(@"(?<{0}>[^\\]*?)", field);
                    else
                        finalRegEx += String.Format(@"(?<{0}>[^\\]*)", field);
                }
                else
                {
                    // other tags coming? put lazy *, otherwise put a greedy one
                    if (SimpleExpression.IndexOf('<', closeTagLocation) != -1)
                        finalRegEx += @"(?:[^\\]*?)";
                    else
                        finalRegEx += @"(?:[^\\]*)";
                }
            }
            return finalRegEx;
        }
        #endregion Regex
    }
}
